<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <meta http-equiv="x-ua-compatible" content="ie=edge">

  <title>RTSPtoWEB</title>
  <link rel="stylesheet" href="/../plugins/fontawesome-free/css/all.min.css">
  <link rel="stylesheet" href="/../css/adminlte.min.css">
  <link rel="stylesheet" href="/../plugins/sweetalert2/sweetalert2.min.css">
  <link rel="stylesheet" href="/../css/index.css">
  <link rel="stylesheet" href="/../css/fullmulti.css">
  <link href="/../css/google-fonts.css" rel="stylesheet">
</head>

<body class="hold-transition layout-top-nav img-background">

  <div class="wrapper">

    <!-- Navbar -->
    <nav class="main-header navbar navbar-expand-md navbar-light navbar-dark">
      <div class="container-fluid">


        <button class="navbar-toggler order-1" type="button" data-toggle="collapse" data-target="#navbarCollapse" aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation">
          <span class="navbar-toggler-icon"></span>
        </button>

        <div class="collapse navbar-collapse order-3" id="navbarCollapse">
          <ul class="order-1 order-md-3 navbar-nav navbar-no-expand ml-auto">
            <input type="hidden" id="defaultPlayer" value="mse" />
            {{ if .query.controls}}
            <li class="nav-item dropdown">
              <a href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" class="nav-link dropdown-toggle"><i class="fas fa-photo-video"></i></a>
              <div class="dropdown-menu dropdown-menu-right custom-dropdown">
                <div class="custom-dropdown-item with-img" onclick="changeBackground('back')">
                  <img src="/../img/back.jpg" />
                </div>
                <div class="custom-dropdown-item with-img" onclick="changeBackground('red')">
                  <img src="/../img/red.jpg" />
                </div>
                <div class="custom-dropdown-item with-img" onclick="changeBackground('green')">
                  <img src="/../img/green.jpg" />
                </div>
                <div class="custom-dropdown-item with-img" onclick="changeBackground('white')">
                  <img src="/../img/white.jpg" />
                </div>
              </div>
            </li>
            {{end}}
            <li class="nav-item dropdown">
              <a id="defaultGrid" href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" class="nav-link dropdown-toggle"><i class="fas fa-th-large"></i></a>
              <div aria-labelledby="defaultGrid" class="dropdown-menu dropdown-menu-right custom-dropdown">
                <div class="custom-dropdown-item grid-maker" grid="4" onclick="gridMaker(4)">
                  2 x 2
                </div>
                <div class="custom-dropdown-item grid-maker" grid="6" onclick="gridMaker(6)">
                  3 x 2
                </div>
                <div class="custom-dropdown-item grid-maker" grid="9" onclick="gridMaker(9)">
                  3 x 3
                </div>
                <div class="custom-dropdown-item grid-maker" grid="12" onclick="gridMaker(12)">
                  4 x 3
                </div>
                <div class="custom-dropdown-item grid-maker" grid="16" onclick="gridMaker(16)">
                  4 x 4
                </div>
                <div class="custom-dropdown-item grid-maker" grid="25" onclick="gridMaker(25)">
                  5 x 5
                </div>
                <div class="custom-dropdown-item grid-maker" grid="36" onclick="gridMaker(36)">
                  6 x 6
                </div>
                <div class="custom-dropdown-item grid-maker" grid="49" onclick="gridMaker(49)">
                  7 x 7
                </div>
              </div>
            </li>
              {{ if .query.controls}}
            <li class="nav-item dropdown">
              <a id="defaultPlayerMenu" href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" class="nav-link dropdown-toggle">MSE</a>
              <ul aria-labelledby="defaultPlayerMenu" class="dropdown-menu  custom-dropdown">
                <li><a href="#" class="dropdown-item" onclick="defaultPlayer('mse',this)">MSE</a></li>
                <li><a href="#" class="dropdown-item" onclick="defaultPlayer('hls',this)">HLS</a></li>
                <li><a href="#" class="dropdown-item" onclick="defaultPlayer('webrtc',this)">WebRTC</a></li>
              </ul>
            </li>
            <li class="nav-item">
              <a class="nav-link" href="/pages/login" role="button"><i class="fas fa-sign-out-alt"></i> Exit</a>
            </li>
            {{end}}
          </ul>
        </div>
      </div>
    </nav>
    <!-- /.navbar -->
    <div class="content-wrapper p-0">
      <div class="content  p-0">
        <div class="container-fluid  p-0" style="overflow: hidden;">
          <div class="grid-wrapper" id="grid-wrapper">

          </div>
          <div class="main-player-wrapper d-none">

            <div class="main-player" data-player="none" data-uuid="0">
              <video class="main-video-player" id="videoPlayer" autoplay muted playsinline></video>
              <div class="play-info"> </div>
              <img class="loader d-none" src="/../img/loader.svg" />
              <canvas id="canvas" class="d-none"></canvas>
              <input type="hidden" id="uuid" value="0" />
            </div>
            <a onclick="closeMain()"><i class="fas fa-times"></i></a>
          </div>


        </div>
      </div>

      <!-- foot -->
    </div>
    <!-- content-wrapper -->
    <!-- STREAMS LIST -->
    <aside class="control-sidebar custom">
      <!-- Control sidebar content goes here -->
      <p>
        <a class="control-sidebar-close-btn pull-right" href="#" onclick="$('.control-sidebar').ControlSidebar('collapse')"><i class="fas fa-times"></i></a>
      </p>

      <h5>Available streams:</h5>
      <input type="hidden" id="player-index" value="0" />
      <div class="row">
        {{ range $key, $value := .streams }}
        <div class="col-12" id="{{ $key }}">
          <div class="card  card-success">
            <div id="carousel_{{$key}}" class="carousel slide" data-ride="carousel">

              <div class="carousel-inner">
                {{if eq (len $value.Channels) 1}}
                <div class="carousel-item  active">
                  <a onclick="play('{{ $key }}',null,0)" href="#"><img class="d-block w-100 stream-img" channel="0" src="/../img/noimage.svg"></a>
                  <div class="carousel-caption d-none d-md-block">
                    <h5>{{$value.Name}}</h5>
                  </div>
                </div>
                {{else}}
                {{ range $k, $v := .Channels }}
                {{ if eq $k "1"}}
                <div class="carousel-item  active">
                  <a onclick="play('{{ $key }}',null,{{$k}})" href="#"><img class="d-block w-100 stream-img" channel="{{$k}}" src="/../img/noimage.svg"></a>
                  <div class="carousel-caption d-none d-md-block">
                    <h5>{{$value.Name}}</h5>
                  </div>
                </div>
                {{end}}
                {{end}}
                {{end}}

              </div>
            </div>

          </div>
        </div>
        {{ end }}
      </div>
    </aside>

  </div>
  <!-- ./wrapper -->
  <script>
    let streams = {{ .streams }};
    let presetOptions = {{ .options }};
    let get_query={{.query}};
  </script>
  <script src="/../plugins/jquery/jquery.min.js"></script>
  <script src="/../plugins/bootstrap/js/bootstrap.bundle.min.js"></script>
  <script src="/../js/adminlte.min.js"></script>
  <script src="/../plugins/sweetalert2/sweetalert2.min.js"></script>
  <script src="/../js/index.js"></script>


</body>

</html>
<!-- end foot     -->
<script src="/../plugins/hlsjs/hls.min.js"></script>
<script>
  let colordebug = false;

  let players = {};
  $(document).ready(() => {
    logger(9, 'page is ready')
    changeBackground(localStorage.getItem('backgroundImage'));
    if('grid' in get_query){
      let get_grid=get_query.grid[0]||4;
      multiviewGrid('set', get_grid);
    }
    if (presetOptions.grid != 0) {
      multiviewGrid('set', presetOptions.grid);
    }

    if (presetOptions.player != null) {
      localStorage.setItem('multiviewPlayers', JSON.stringify(presetOptions.player));
    }
    gridMaker(multiviewGrid('get'));
    restoreStreams();
  });

  function defaultPlayer(type, el) {
    $('#defaultPlayer').val(type);
    $(el).closest('.nav-item').children('a').html($(el).text());
  }

  function gridMaker(col = 4) {
    let grid_sizes=[4,6,9,12,16,25,36,49];
    col = parseInt(col);
    col=grid_sizes.filter(v => v >= col)[0]||4;
    let colW;
    switch (col) {
      case 6:
        colW = 'grid-6';
        break;
      case 9:
        colW = 'grid-9';
        break;
      case 12:
        colW = 'grid-12';
        break;
      case 16:
        colW = 'grid-16';
        break;

      case 25:
        colW = 'grid-25';
        break;
      case 36:
        colW = 'grid-36';
        break;
      case 49:
        colW = 'grid-49';
        break;
      default:
        colW = '';
        break;
    }
    let memory = localStorage.getItem('multiviewPlayers');
    destroyGrid();
    for (var i = 0; i < col; i++) {
      $('#grid-wrapper').append(
        `<div class=" player ` + colW + ` empty" data-player="none" data-uuid="0">
              <div class="play-info"></div>
              <video class="video-class empty" autoplay muted playsinline></video>
            ` +
        `<a class="remove-btn" onclick="destoyPlayer(` + i + `)"><i class="fas fa-times"></i></a>` +
        `<a class="add-stream-btn" onclick="openChoise(this)"><i class="fas fa-plus"></i><br />Click to select stream</a>` +
        `<a href="#" class="btn  btn-info btn-xs btn-play-main d-none"  onclick="playMainStream(this)"><i class="fas fa-expand"></i> Expand</a>` +
        `<img class="loader d-none" src="/../img/loader.svg" />` +
        `</div>`);
    }
    multiviewGrid('set', col);
    addEventListenerToVideo();
    $('.grid-maker').removeClass('active');
    $('.grid-maker[grid="' + col + '"]').addClass('active');
    if (memory != null) {
      localStorage.setItem('multiviewPlayers', memory);
      restoreStreams();
    }

  }

  function addEventListenerToVideo() {
    $('.video-class').each(function() {
      let _this = this;
      let index = $(this).closest('.player').index();
      let uuid = $(this).closest('.player').attr('data-uuid');

      this.addEventListener('loadeddata', () => {
        _this.play();
        makePic(this, $(_this).closest('.player').attr('data-uuid'), 1);
        logger(index, '[video]: loadeddata');
      });
      this.addEventListener('stalled', () => {
        logger(index, '[video]: stalled');
      });
      this.addEventListener('pause', () => {
        if (_this.currentTime > _this.buffered.end(_this.buffered.length - 1)) {
          _this.currentTime = _this.buffered.end(_this.buffered.length - 1) - 0.1;
          _this.play();
        }
        logger(index, '[video]: pause');
      });

      this.addEventListener('error', (e) => {
        logger(index, '[video]: error', e);
        //console.log(e);
      });

      this.addEventListener('abort', () => {
        logger(index, '[video]: abort');
      });

      this.addEventListener('emptied', (e) => {
        logger(index, '[video]: emptied');
      });

      this.addEventListener('ended', (e) => {
        logger(index, '[video]: ended');
      });

      this.addEventListener('play', (e) => {
        logger(index, '[video]: play');
      });

      this.addEventListener('suspend', (e) => {
        logger(index, '[video]: suspend');
      });
      this.addEventListener('waiting', (e) => {
        logger(index, '[video]: waiting');
      });
      this.addEventListener('canplaythrough', (e) => {
        $(_this).closest('div').find('.loader').addClass('d-none');
        logger(index, '[video]: canplaythrough');
      });
      this.addEventListener('playing', (e) => {
        $(_this).closest('div').find('.loader').addClass('d-none');
        logger(index, '[video]: playing');
      });
      this.addEventListener('loadedmetadata', (e) => {
        logger(index, '[video]: loadedmetadata');
      });
      this.addEventListener('loadstart', (e) => {
        logger(index, '[video]: loadstart');
      });
    });
  }

  $('.main-video-player')[0].addEventListener('canplaythrough', (e) => {
    $('.main-player').find('.loader').addClass('d-none');
    logger(0, '[video]: canplaythrough');
  });
  $('.main-video-player')[0].addEventListener('playing', (e) => {
    $('.main-player').find('.loader').addClass('d-none');
    logger(0, '[video]: canplaythrough');
  });

  $("#videoPlayer")[0].addEventListener('loadeddata', () => {
    $("#videoPlayer")[0].play();
    let browser = browserDetector();
    if (!browser.safari) {
      makePic();
    }
  });

  function destroyGrid() {
    $('.player').each(function(index) {
      destoyPlayer(index);
    });
    $('#grid-wrapper').empty();
  }

  function openChoise(dom) {
    $('#player-index').val($(dom).closest('.player').index());
    $('.control-sidebar').ControlSidebar('show')

  }

  function play(uuid, index, chan, typePlayer) {
    $('#uuid').val(uuid);
    if (typeof(index) == 'undefined' || index == null) {
      index = $('#player-index').val();
    }
    let videoPlayerVar = $('.main-player');
    if (index != 'main') {
      videoPlayerVar = $('.player').eq(index);
    }

    $('.control-sidebar').ControlSidebar('collapse');
    destoyPlayer(index);
    videoPlayerVar.removeClass('empty');
    videoPlayerVar.find('video').removeClass('empty');


    let playerType = $('#defaultPlayer').val();
    if (!!typePlayer) {
      playerType = typePlayer;
    }
    videoPlayerVar.attr('data-player', playerType);
    videoPlayerVar.attr('data-uuid', uuid);

    let channel = 0;

    if (typeof(streams[uuid].channels[1]) !== "undefined") {
      channel = 1;
    }

    if (index == 'main') {
      channel = 0;
    } else {
      packStreamms(index, uuid, chan, playerType);
    }
    if (colordebug) {
      videoPlayerVar.find('.play-info').html('Stream: ' + streams[uuid].name + ' | player type:' + playerType + ' | channel: ' + channel);
    }
    videoPlayerVar.find('.loader').removeClass('d-none');
    //fix stalled video in safari
    videoPlayerVar.find('video')[0].addEventListener('pause', () => {
      if (videoPlayerVar.find('video')[0].currentTime > videoPlayerVar.find('video')[0].buffered.end((videoPlayerVar.find('video')[0].buffered.length - 1))) {
        videoPlayerVar.find('video')[0].currentTime = videoPlayerVar.find('video')[0].buffered.end((videoPlayerVar.find('video')[0].buffered.length - 1)) - 0.1;
        videoPlayerVar.find('video')[0].play();
      }
    });

    switch (playerType) {
      case 'hls':
        let url = '/stream/' + uuid + '/channel/' + channel + '/hls/live/index.m3u8';

        if (videoPlayerVar.find('video')[0].canPlayType('application/vnd.apple.mpegurl')) {
          videoPlayerVar.find('video')[0].src = url;
          videoPlayerVar.find('video')[0].load();
        } else if (Hls.isSupported()) {
          players[index] = new Hls({
            manifestLoadingTimeOut: 60000
          });
          players[index].loadSource(url);
          players[index].attachMedia(videoPlayerVar.find('video')[0]);
        } else {
          Swal.fire({
            icon: 'error',
            title: 'Oops...',
            text: 'Your browser don`t support hls '
          });
        }
        break;
      case 'webrtc':
        players[index] = new WebRTCPlayer(uuid, videoPlayerVar, channel);
        players[index].playWebrtc();
        break;
      case 'mse':
      default:

        players[index] = new msePlayer(uuid, videoPlayerVar, channel);
        players[index].playMse();
        break;
    }

  }

  function destoyPlayer(index) {
    let videoPlayerVar = $('.main-player');
    if (index != 'main') {
      videoPlayerVar = $('.player').eq(index);
    }
    let type = videoPlayerVar.attr('data-player');
    videoPlayerVar.addClass('empty');
    videoPlayerVar.find('video').addClass('empty')

    switch (type) {
      case 'hls':
        if (!!players[index]) {
          players[index].destroy();
          delete players[index];
        }
        break;
      case 'mse':
        players[index].destroy();
        delete players[index];

        break;
      case 'webrtc':
        players[index].destroy();
        delete players[index];
        break;
      default:

        break;
    }
    videoPlayerVar.attr('data-player', 'none');
    videoPlayerVar.attr('data-uuid', 0);
    videoPlayerVar.find('.play-info').html('');
    videoPlayerVar.find('video')[0].src = '';
    videoPlayerVar.find('video')[0].load();
    videoPlayerVar.find('.loader').addClass('d-none');

    unpackStreams(index);
  }

  function expand(element) {
    fullscreenOn($('#grid-wrapper').parent()[0]);
  }

  function playMainStream(element) {
    let uuid = $(element).closest('.player').attr('data-uuid');
    if (uuid == 0) {
      return;
    }
    $('.main-player-wrapper').removeClass('d-none');
    play(uuid, 'main');
  }

  function closeMain() {
    destoyPlayer('main');
    $('.main-player-wrapper').addClass('d-none');
  }
  /*************************mse obect **************************/
  function msePlayer(uuid, videoPlayerVar, channel) {
    this.ws = null,
      this.video = videoPlayerVar.find('video')[0],
      this.mseSourceBuffer = null,
      this.mse = null,
      this.mseQueue = [],
      this.mseStreamingStarted = false,
      this.uuid = uuid,
      this.channel = channel || 0;
    this.timeout = null;
    this.checktime = null;
    this.checktimecounter = 0;
    this.playMse = function() {
      let _this = this;
      logger(videoPlayerVar.index(),
        'func playMse',
        'streams: ' + uuid,
        'channel: ' + channel);

      this.mse = new MediaSource();
      this.video.src = window.URL.createObjectURL(this.mse);

      let potocol = 'ws';
      if (location.protocol == 'https:') {
        potocol = 'wss';
      }

      let ws_url = potocol + '://' + location.host + '/stream/' + this.uuid + '/channel/' + this.channel + '/mse?uuid=' + this.uuid + '&channel=' + this.channel;

      this.mse.addEventListener('sourceopen', function() {
        logger(videoPlayerVar.index(),
          uuid,
          channel,
          '[MSE]: sourceopen');
        _this.ws = new WebSocket(ws_url);
        _this.ws.binaryType = "arraybuffer";
        _this.ws.onopen = function(event) {

          logger(videoPlayerVar.index(),
            uuid,
            channel,
            '[websocket]: connected');
        }

        _this.ws.onclose = function(event) {
          logger(videoPlayerVar.index(),
            uuid,
            channel,
            '[websocket]: closed');
          if (_this.timeout != null) {
            clearInterval(_this.timeout);
            _this.timeout = null;
          }
          _this.timeout = setTimeout(() => {
            logger(videoPlayerVar.index(),
              uuid,
              channel,
              '[websocket]: timeouted func play');
            play(uuid, videoPlayerVar.index(), channel, 'mse')
          }, 15000)


        }
        _this.ws.onerror = (e) => {
          logger(videoPlayerVar.index(),
            uuid,
            channel,
            '[websocket]: error');
        }
        _this.ws.onmessage = function(event) {
          _this.checkStalled();
          let data = new Uint8Array(event.data);
          if (data[3] == 24) {
            logger(videoPlayerVar.index(),
              uuid,
              channel,
              '[data]: init_file');
          }

          if (data[0] == 9) {
            decoded_arr = data.slice(1);
            if (window.TextDecoder) {
              mimeCodec = new TextDecoder("utf-8").decode(decoded_arr);
            } else {
              mimeCodec = Utf8ArrayToStr(decoded_arr);
            }
            logger(videoPlayerVar.index(),
              uuid,
              channel,
              '[codec]: ' + mimeCodec);
            //console.log(mimeCodec);
            _this.mseSourceBuffer = _this.mse.addSourceBuffer('video/mp4; codecs="' + mimeCodec + '"');
            _this.mseSourceBuffer.mode = "segments"
            _this.mseSourceBuffer.addEventListener("updateend", _this.pushPacket.bind(_this));

          } else {
            _this.readPacket(event.data);
          }
        };
      }, false);

      this.mse.addEventListener('sourceended', function() {
        logger(videoPlayerVar.index(),
          uuid,
          channel,
          '[MSE]: sourceended');
      })
      this.mse.addEventListener('sourceclose', function() {
        logger(videoPlayerVar.index(),
          uuid,
          channel,
          '[MSE]: sourceclose');
      })

      this.mse.addEventListener('error', function() {
        logger(videoPlayerVar.index(),
          uuid,
          channel,
          '[MSE]: error');
      })
      this.mse.addEventListener('abort', function() {
        logger(videoPlayerVar.index(),
          uuid,
          channel,
          '[MSE]: abort');
      })
      this.mse.addEventListener('updatestart', function() {
        logger(videoPlayerVar.index(),
          uuid,
          channel,
          '[MSE]: updatestart');
      })
      this.mse.addEventListener('update', function() {
        logger(videoPlayerVar.index(),
          uuid,
          channel,
          '[MSE]: update');
      })
      this.mse.addEventListener('updateend', function() {
        logger(videoPlayerVar.index(),
          uuid,
          channel,
          '[MSE]: updateend');
      })
      this.mse.addEventListener('addsourcebuffer', function() {
        logger(videoPlayerVar.index(),
          uuid,
          channel,
          '[MSE]: addsourcebuffer');
      })
      this.mse.addEventListener('removesourcebuffer', function() {
        logger(videoPlayerVar.index(),
          uuid,
          channel,
          '[MSE]: removesourcebuffer');
      })

    }

    this.readPacket = function(packet) {
        if (!this.mseStreamingStarted) {
          try {
            this.mseSourceBuffer.appendBuffer(packet);
            this.mseStreamingStarted = true;
          } catch (e) {
            logger(videoPlayerVar.index(),
              'readPacket error',
              'streams: ' + uuid,
              'channel: ' + channel);
            console.log(e);

            play(uuid, videoPlayerVar.index(), channel, 'mse');

          } finally {
            return;
          }


        }
        this.mseQueue.push(packet);

        if (!this.mseSourceBuffer.updating) {
          this.pushPacket();
        }
      },

      this.pushPacket = function() {
        let _this = this;
        if (!_this.mseSourceBuffer.updating) {
          if (_this.mseQueue.length > 0) {
            packet = _this.mseQueue.shift();

            try {
              _this.mseSourceBuffer.appendBuffer(packet)
            } catch (e) {
              logger(videoPlayerVar.index(),
                'pushPacket error',
                'streams: ' + uuid,
                'channel: ' + channel);
              console.log(e);

              play(uuid, videoPlayerVar.index(), channel, 'mse');
            } finally {

            }
          } else {
            _this.mseStreamingStarted = false;
          }
        }
        if (_this.video.buffered.length > 0) {
          if (typeof document.hidden !== "undefined" && document.hidden) {
            _this.video.currentTime = _this.video.buffered.end((_this.video.buffered.length - 1)) - 0.5;
          } else {
            if ((_this.video.buffered.end((_this.video.buffered.length - 1)) - _this.video.currentTime) > 60) {
              _this.video.currentTime = _this.video.buffered.end((_this.video.buffered.length - 1)) - 0.5;
            }
          }
        }
      }
    this.checkStalled = function() {
        if (!!this.video.currentTime) {
          if (this.video.currentTime == this.checktime) {
            this.checktimecounter += 1;
          } else {
            this.checktimecounter = 0;
          }
        }
        if (this.checktimecounter > 10) {
          logger(videoPlayerVar.index(),
            uuid,
            channel,
            '[FIX]: player not move');
          play(uuid, videoPlayerVar.index(), channel, 'mse');
        }
        this.checktime = this.video.currentTime;

      },
      this.destroy = function() {
        if (this.timeout != null) {
          clearInterval(this.timeout);
        }
        if (this.ws != null) {

          this.ws.onclose = null;
          this.ws.close(1000, "stop streaming");
        }



        logger(videoPlayerVar.index(),
          'Event: PlayerDestroy',
          'streams: ' + uuid,
          'channel: ' + channel);
      }
  }
  /*************************end mse obect **************************/
  /*************************WEBRTC obect **************************/
  function WebRTCPlayer(uuid, videoPlayerVar, channel) {
    this.webrtc = null;
    this.webrtcSendChannel = null;
    this.webrtcSendChannelInterval = null;
    this.uuid = uuid;
    this.video = videoPlayerVar.find('video')[0];
    this.channel = channel || 0;
    this.playWebrtc = function() {
        var _this = this;
        this.webrtc = new RTCPeerConnection({
          iceServers: [{
            urls: ["stun:stun.l.google.com:19302"]
          }]
        });
        this.webrtc.onnegotiationneeded = this.handleNegotiationNeeded.bind(this);
        this.webrtc.ontrack = function(event) {
          console.log(event.streams.length + ' track is delivered');
          _this.video.srcObject = event.streams[0];
          _this.video.play();
        }
        this.webrtc.addTransceiver('video', {
          'direction': 'sendrecv'
        });
        this.webrtcSendChannel = this.webrtc.createDataChannel('foo');
        this.webrtcSendChannel.onclose = (e) => console.log('sendChannel has closed', e);
        this.webrtcSendChannel.onopen = () => {
          console.log('sendChannel has opened');
          this.webrtcSendChannel.send('ping');
          this.webrtcSendChannelInterval = setInterval(() => {
            this.webrtcSendChannel.send('ping');
          }, 1000)
        }

        this.webrtcSendChannel.onmessage = e => console.log(e.data);
      },
      this.handleNegotiationNeeded = async function() {
        var _this = this;

        offer = await _this.webrtc.createOffer();
        await _this.webrtc.setLocalDescription(offer);
        $.post("/stream/" + _this.uuid + "/channel/" + this.channel + "/webrtc?uuid=" + _this.uuid + "&channel=" + this.channel, {
          data: btoa(_this.webrtc.localDescription.sdp)
        }, function(data) {
          try {
            _this.webrtc.setRemoteDescription(new RTCSessionDescription({
              type: 'answer',
              sdp: atob(data)
            }))
          } catch (e) {
            console.warn(e);
          }

        });
      }

    this.destroy = function() {
      clearInterval(this.webrtcSendChannelInterval);
      this.webrtc.close();
      this.video.srcObject = null;
    }
  }

  /*********************FULSCREEN******************/
  function fullscreenEnabled() {
    return !!(
      document.fullscreenEnabled ||
      document.webkitFullscreenEnabled ||
      document.mozFullScreenEnabled ||
      document.msFullscreenEnabled
    );
  }

  function fullscreenOn(elem) {
    if (elem.requestFullscreen) {
      elem.requestFullscreen();
    } else if (elem.mozRequestFullScreen) {
      elem.mozRequestFullScreen();
    } else if (elem.webkitRequestFullscreen) {
      elem.webkitRequestFullscreen();
    } else if (elem.msRequestFullscreen) {
      elem.msRequestFullscreen();
    }
  }

  function fullscreenOff() {
    if (document.requestFullscreen) {
      document.requestFullscreen();
    } else if (document.webkitRequestFullscreen) {
      document.webkitRequestFullscreen();
    } else if (document.mozRequestFullscreen) {
      document.mozRequestFullScreen();
    }
  }

  function packStreamms(index, uuid, channel, type) {
    let multiviewPlayers;
    if (localStorage.getItem('multiviewPlayers') != null) {
      multiviewPlayers = JSON.parse(localStorage.getItem('multiviewPlayers'));
    } else {
      multiviewPlayers = {};
    }
    multiviewPlayers[index] = {
      uuid: uuid,
      channel: channel,
      playerType: type
    }
    localStorage.setItem('multiviewPlayers', JSON.stringify(multiviewPlayers));
  }

  function unpackStreams(index) {
    if (localStorage.getItem('multiviewPlayers') != null) {
      let multiviewPlayers = JSON.parse(localStorage.getItem('multiviewPlayers'));
      delete multiviewPlayers[index];
      localStorage.setItem('multiviewPlayers', JSON.stringify(multiviewPlayers));
    }
  }

  function restoreStreams() {
    if (localStorage.getItem('multiviewPlayers') != null) {

      let multiviewPlayers = JSON.parse(localStorage.getItem('multiviewPlayers'));
      if (Object.keys(multiviewPlayers).length > 0) {
        $.each(multiviewPlayers, function(key, val) {

          if (val.uuid in streams && val.channel in streams[val.uuid].channels) {
            if ($('.player').eq(key).length > 0) {
              play(val.uuid, key, val.channel, val.playerType);
            }

          } else {
            unpackStreams(key);
          }
        })
      }

    } else {

      if (Object.keys(streams).length > 0) {
        gridMaker(Object.keys(streams).length);
        let playerIndex = 0;
        let gridSize = $('.player').length;
        $.each(streams, function(uuid, params) {
          //console.log(i,uuid);
          if (playerIndex < gridSize) {
            let channel = 0;
            if ('1' in params.channels) {
              channel = 1;
            }
            play(uuid, playerIndex, channel, 'mse');
            playerIndex++;
          } else {
            return;
          }


        })
      }

    }
  }

  function multiviewGrid(type, grid) {
    let defGrid = 25;
    switch (type) {
      case 'set':
        localStorage.setItem('multiviewGrid', grid);
        break;
      case 'get':
        if (localStorage.getItem('multiviewGrid') != null) {
          return localStorage.getItem('multiviewGrid');
        } else {
          return defGrid
        }
        break;
      default:
        return defGrid
    }
    return defGrid
  }

  $('#grid-wrapper').on('dblclick', '.player', function() {
    $(this).find('.btn-play-main').click();
  });
  $('.main-player').on('dblclick', function() {
    closeMain()
  });

  function changeBackground(num) {
    let back = '/../img/';
    localStorage.setItem('backgroundImage', num);
    switch (num) {
      case 'green':
        back += 'green.jpg'
        break;
      case 'red':
        back += 'red.jpg'
        break;
      case 'white':
        back += 'white.jpg'
        break;
      default:
        back += 'back.jpg'
    }
    $('.img-background').css('background-image', 'url("' + back + '")');
  }
</script>
